﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using WMM.Tapioca.Linkbase;
using Saxon.Api;
using System.Text.RegularExpressions;
using WMM.Tapioca.Formula;
using WMM.Tapioca.Formula.Aspects;
using WMM.Tapioca.Formula.Filters;
using WMM.Tapioca.Instance;
using WMM.Tapioca.Utilities;
using WMM.Tapioca.Taxonomy;
using System.Xml;
using System.Collections;
using WMM.Tapioca.Formula.Functions;
using WMM.Tapioca.XbrlDimension;

namespace WMM.Tapioca.Formula
{
    /// <summary>
    /// A formula expresses a set of rules for constructing an output XBRL fact by transforming the values 
    /// that the variables in the formula's variable set have evaluated to.
    /// </summary>
    public class FormulaXBRL : VariableSet
    {
        XNamespace formula = "http://xbrl.org/2008/formula";
        XNamespace xbrli = Constants.XBRLI;
        XNamespace xbrldi = Constants.XBRLDI;               

        private string value;

        /// <returns> the value of output facts. </returns>
        public string Value
        {
            get { return this.value; }
            set { this.value = value; }
        }

        private string source;

        /// <returns>An aspect may be obtained (in part or full) from a bound variable of the evaluation, 
        /// by specifying a source. Source may be specified on a rule, and may be inherited from a source
        /// on the formula (or tuple) element. When there are multiple sources, the nearest one to an
        /// aspect rule prevails. </returns>
        public string Source
        {
            get { return source; }
            set { source = value; }
        }

        private string precisionAspect;

        public string PrecisionAspect
        {
            get { return precisionAspect; }
            set { precisionAspect = value; }
        }

        private string precision;

        /// <returns> the accuracy of numeric output facts. </returns>
        public string Precision
        {
            get { return precision; }
            set { precision = value; }
        }

        private string decimalAspect;

        public string DecimalAspect
        {
            get { return decimalAspect; }
            set { decimalAspect = value; }
        }

        private string decimals;

        /// <returns> the decimal of numeric output facts. </returns>
        public string Decimals
        {
            get { return decimals; }
            set { decimals = value; }
        }

        private ConceptAspect conceptAspectFormula;

        /// <returns> Content of attribute "use". </returns>
        public ConceptAspect ConceptAspectFormula
        {
            get { return conceptAspectFormula; }
            set { conceptAspectFormula = value; }
        }

        private EntityIdentifierAspect entityIdentifierAspectFormula;

        /// <returns> Content of attribute "use". </returns>
        public EntityIdentifierAspect EntityIdentifierAspectFormula
        {
            get { return entityIdentifierAspectFormula; }
            set { entityIdentifierAspectFormula = value; }
        }

        private PeriodAspect periodAspectFormula;

        /// <returns> Content of attribute "use". </returns>
        public PeriodAspect PeriodAspectFormula
        {
            get { return periodAspectFormula; }
            set { periodAspectFormula = value; }
        }

        private UnitAspect unitAspectFormula;

        /// <returns> Content of attribute "use". </returns>
        public UnitAspect UnitAspectFormula
        {
            get { return unitAspectFormula; }
            set { unitAspectFormula = value; }
        }

        private List<ExplicitDimensionAspect> explicitDimensionAspectFormulas;

        /// <returns> Content of attribute "use". </returns>
        public List<ExplicitDimensionAspect> ExplicitDimensionAspectFormulas
        {
            get { return explicitDimensionAspectFormulas; }
            set { explicitDimensionAspectFormulas = value; }
        }

        private List<TypedDimensionAspect> typedDimensionAspectFormulas;

        /// <returns> Content of attribute "use". </returns>
        public List<TypedDimensionAspect> TypedDimensionAspectFormulas
        {
            get { return typedDimensionAspectFormulas; }
            set { typedDimensionAspectFormulas = value; }
        }

        private OccFragmentsAspect occFragmentsAspectFormula;

        /// <returns> Content of attribute "use". </returns>
        public OccFragmentsAspect OccFragmentsAspectFormula
        {
            get { return occFragmentsAspectFormula; }
            set { occFragmentsAspectFormula = value; }
        }

        private OccEmptyAspect occEmptyAspectFormula;

        /// <returns> Content of attribute "use". </returns>
        public OccEmptyAspect OccEmptyAspectFormula
        {
            get { return occEmptyAspectFormula; }
            set { occEmptyAspectFormula = value; }
        }

        private OccXpathAspect occXpathAspectFormula;

        /// <returns> Content of attribute "use". </returns>
        public OccXpathAspect OccXpathAspectFormula
        {
            get { return occXpathAspectFormula; }
            set { occXpathAspectFormula = value; }
        }

        private XbrlDocument parentDocument;

        /// <returns> Content of attribute "use". </returns>
        public XbrlDocument ParentDocument
        {
            get { return parentDocument; }
            set { parentDocument = value; }
        }

        private List<string> coveredAspects;

        /// <returns> List of covered aspects. </returns>
        public List<string> CoveredAspects
        {
            get { return coveredAspects; }
            set { coveredAspects = value; }
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        public FormulaXBRL(Resource resourceFormula, XbrlDocument parentDocument, Dictionary<string, string> namespaces)
            : base(resourceFormula, namespaces)
        {
            this.parentDocument = parentDocument;

            RegisterExtensionFunctions();

            explicitDimensionAspectFormulas = new List<ExplicitDimensionAspect>();
            typedDimensionAspectFormulas = new List<TypedDimensionAspect>();
            coveredAspects = new List<string>();

            foreach (XAttribute attr in resourceFormula.ResourceXElement.Attributes())
            {
                if (attr.Name.LocalName.Equals("value"))
                {
                    string expression = attr.Value;
                    expression = expression.TrimEnd().TrimStart();
                    expression = Regex.Replace(expression, @"\s+", " ");
                    value = expression;
                }

                if (attr.Name.LocalName.Equals("source"))
                {
                    string qname = attr.Value;
                    qname = qname.TrimEnd().TrimStart();
                    qname = Regex.Replace(qname, @"\s+", " ");
                    source = qname;
                }

                if (attr.Name.LocalName.Equals("aspectModel"))
                {
                    AspectModel = attr.Value;
                }

                if (attr.Name.LocalName.Equals("implicitFiltering"))
                {
                    ImplicitFiltering = Boolean.Parse(attr.Value);
                }
            }


            foreach (Arc arc in resourceFormula.ExtendedLinkParent.Arcs)
            {
                if (arc.FromId.Equals(Label) && arc.ArcRole.AbsoluteUri.Equals("http://xbrl.org/arcrole/2008/variable-set"))
                {
                    VariableSetArcs.Add(new VariableSet(arc));

                    List<Resource> resources = GetResources(arc.ToId, resourceFormula.ExtendedLinkParent);

                    foreach (Resource resource in resources)
                    {

                        if (resource.LocalName.Equals("parameter"))
                        {
                            Parameters.Add(new Parameter(resource));
                        }

                        if (resource.LocalName.Equals("factVariable"))
                        {
                            FactVariables.Add(new FactVariable(resource));
                        }

                        if (resource.LocalName.Equals("generalVariable"))
                        {
                            GeneralVariables.Add(new GeneralVariable(resource));
                        }
                    }
                }

                if (arc.FromId.Equals(Label) && arc.ArcRole.AbsoluteUri.Equals("http://xbrl.org/arcrole/2008/variable-set-precondition"))
                {
                    Resource resource = GetResource(arc.ToId, resourceFormula.ExtendedLinkParent);

                    if (resource.LocalName.Equals("precondition"))
                    {
                        Precondition = new Precondition(resource);
                    }
                }

                if (arc.FromId.Equals(Label) && arc.ArcRole.AbsoluteUri.Equals("http://xbrl.org/arcrole/2008/element-label"))
                {
                    LabelAssertion = GetResource(arc.ToId, resourceFormula.ExtendedLinkParent);
                }                   
            }

            foreach (Arc arc in resourceFormula.ExtendedLinkParent.Arcs)
            {
                if (arc.ArcRole.AbsoluteUri.Equals("http://xbrl.org/arcrole/2008/variable-filter"))
                {
                    foreach (FactVariable factVariable in FactVariables)
                    {
                        if (factVariable.Label.Equals(arc.FromId))
                        {
                            List<Resource> filters = GetResources(arc.ToId, arc.ExtendedLinkParent);

                            foreach (Resource filterResource in filters)
                            {
                                Filter filter = Filter.CreateFilter(arc, filterResource, this);
                                factVariable.Filters.Add(filter);

                                factVariable.VariableFilterArcs.Add(arc);

                                if (filter.Cover)
                                {
                                    AddCoveredAspect(factVariable, arc);
                                }
                            }
                        }
                    }
                }
            }

            foreach (Arc arc in resourceFormula.ExtendedLinkParent.Arcs)
            {
                if (arc.ArcRole.AbsoluteUri.Equals("http://xbrl.org/arcrole/2008/variable-set-filter"))
                {
                    Resource filterResource = GetResource(arc.ToId, arc.ExtendedLinkParent);
                    GroupFilters.Add(Filter.CreateFilter(arc, filterResource, this));
                }
            }

            // ====== Aspects ======

            BuildAspectsRules(resourceFormula);
        }


        private void BuildAspectsRules(Resource resourceFormula)
        {
            foreach (XElement element in resourceFormula.ResourceXElement.Elements())
            {
                if (element.Name.LocalName.Equals("precision"))
                {
                    precisionAspect = element.Value;
                }

                if (element.Name.LocalName.Equals("decimals"))
                {
                    decimalAspect = element.Value;
                }

                if (element.Name.LocalName.Equals("aspects"))
                {
                    XElement conceptAspect = element.Element(formula + "concept");

                    if (conceptAspect != null)
                    {
                        conceptAspectFormula = new ConceptAspect(conceptAspect, parentDocument);
                        coveredAspects.Add("conceptFilter");
                    }

                    XElement entityIdentifierAspect = element.Element(formula + "entityIdentifier");

                    if (entityIdentifierAspect != null)
                    {
                        entityIdentifierAspectFormula = new EntityIdentifierAspect(entityIdentifierAspect);
                        coveredAspects.Add("identifier");
                    }

                    XElement periodAspect = element.Element(formula + "period");

                    if (periodAspect != null)
                    {
                        periodAspectFormula = new PeriodAspect(periodAspect);
                        coveredAspects.Add("period");
                    }

                    XElement unitAspect = element.Element(formula + "unit");

                    if (unitAspect != null)
                    {
                        unitAspectFormula = new UnitAspect(unitAspect);
                        coveredAspects.Add("measure");
                    }

                    IEnumerable<XElement> explicitDimensionAspects = element.Elements(formula + "explicitDimension");

                    if (explicitDimensionAspects != null)
                    {
                        foreach (XElement explicitDimensionAspect in explicitDimensionAspects)
                        {
                            ExplicitDimensionAspect explicitDimensionAspectFormula = new ExplicitDimensionAspect(explicitDimensionAspect, this);
                            coveredAspects.Add("explicitDimension");
                        }
                    }

                    IEnumerable<XElement> typedDimensionAspects = element.Elements(formula + "typedDimension");

                    if (typedDimensionAspects != null)
                    {
                        foreach (XElement typedDimensionAspect in typedDimensionAspects)
                        {
                            TypedDimensionAspect typedDimensionAspectFormula = new TypedDimensionAspect(typedDimensionAspect, this);
                            coveredAspects.Add("typedDimension");
                        }
                    }

                    XElement occFragmentsAspect = element.Element(formula + "occFragments");

                    if (occFragmentsAspect != null)
                    {
                        occFragmentsAspectFormula = new OccFragmentsAspect(occFragmentsAspect);
                        coveredAspects.Add(occFragmentsAspect.Name.LocalName);
                    }

                    XElement occEmptyAspect = element.Element(formula + "occEmpty");

                    if (occEmptyAspect != null)
                    {
                        occEmptyAspectFormula = new OccEmptyAspect(occEmptyAspect);
                        coveredAspects.Add(occEmptyAspect.Name.LocalName);
                    }

                    XElement occXpathAspect = element.Element(formula + "occXpath");

                    if (occXpathAspect != null)
                    {
                        occXpathAspectFormula = new OccXpathAspect(occXpathAspect);
                        coveredAspects.Add(occXpathAspect.Name.LocalName);
                    }
                }
            }
        }

        int k = 0;

        internal void EvaluateFormula(InstanceDocument instance, FormulaProcessor formulaProcessor, int k)
        {
            this.k = k;
            this.dts = instance.Dts;
            this.namespaces = dts.DtsFormula.Namespaces;

            // clear
            variableItems.Clear();
            variableAsSequenceEntries.Clear();
            variableAsItem.Clear();
            variableNamesAsItem.Clear();
            parameterDictionary.Clear();
            dependencyGraph = new Graph();
            
            // Declare variable 
            this.DeclareVariables(this, compiler);

            if (dts.Instance != null)
            {
                compiler.BaseUri = GetPath(dts.Instance.ThisXDocument.BaseUri);
            }

            XPathSelector selector = compiler.Compile(this.Value).Load();

            this.SetContextItem(processor, selector, instance.ThisXDocument.Root.GetXmlNode());            

            this.BindParameters(selector, this, parameterDictionary);

            variableItems.Clear();

            Dictionary<string, XdmAtomicValue> variableFallBack = new Dictionary<string, XdmAtomicValue>();

            // Evaluate variable set

            try
            {
                EvaluateFactVariables(this.variableItems, this.variableSequences, variableFallBack);
            }
            catch (Exception ex)
            {
                log4net.GlobalContext.Properties["assertionId"] = this.Id + " (evaluation " + k + ")";
                log4net.GlobalContext.Properties["satisfied"] = "Exception";
                FormulaProcessor.log.Error("Assertion:" + this.Id + " message:" + ex.Message);
                formulaProcessor.assertionsNotSatisfied++;
                k = k + 1;
                return;
            }

            if (variableItems.Count == 0 && variableSequences.Count == 0)
            {
                log4net.GlobalContext.Properties["satisfied"] = "Not Evaluated";
                FormulaProcessor.log.Error("Assertion:" + this.Id + " message: return 0 elements");
                formulaProcessor.assertionsNotSatisfied++;
                return;
            }

            if ((variableItems.Count + parameterDictionary.Count + variableFallBack.Count + variableSequences.Count) < this.VariableSetArcs.Count)
            {
                return;
            }

            for (int i = 0; i < variableItems.Count; i++)
            {
                variableAsItem.Add(variableItems.ElementAt(i).Value);
                variableNamesAsItem.Add(variableItems.ElementAt(i).Key);                
            }

            List<List<FactItem>> cartesianProductItems = ConvertToList(MyExtensions.CartesianProduct(variableAsItem).ToList());            

            foreach (List<FactItem> cartesianProductItem in cartesianProductItems)
            {
                Dictionary<string, FactItem> variableNotSequenceEntries = new Dictionary<string, FactItem>();

                variableAsSequenceEntries.Clear();

                if (this.ImplicitFiltering)
                {
                    if (this.ImplicitlyMatchItems(cartesianProductItem, variableNamesAsItem, variableSequences, this))
                    {
                        // Bind Variable

                        BindVariable(FormulaXBRL.processor, selector, cartesianProductItem, variableNamesAsItem, variableAsSequenceEntries, variableNotSequenceEntries, variableFallBack);

                        // Evaluate Precondition

                        Boolean precondition = EvaluatePrecondition(compiler, variableNotSequenceEntries, this);

                        log4net.GlobalContext.Properties["assertionId"] = this.Id + " (evaluation " + k + ")";

                        if (precondition)
                        {
                            // Evaluate the XPath expression

                            ProduceOutputFact(formulaProcessor, selector.Evaluate(), cartesianProductItem, variableItems, variableSequences, variableNotSequenceEntries);
                        }
                        else
                        {
                            formulaProcessor.formulaNotSatisfied++;
                        }
                    }
                }
                else
                {
                    foreach (string sequenceName in variableSequences.Keys)
                    {
                        foreach (List<FactItem> currentSequence in variableSequences[sequenceName])
                        {
                            variableNotSequenceEntries.Clear();
                            variableAsSequenceEntries.Clear();

                            variableAsSequenceEntries.Add(sequenceName, currentSequence);

                            // Bind Variable

                            BindVariable(FormulaXBRL.processor, selector, cartesianProductItem, variableNamesAsItem, variableAsSequenceEntries, variableNotSequenceEntries, variableFallBack);

                            // Evaluate Precondition

                            Boolean precondition = EvaluatePrecondition(compiler, variableNotSequenceEntries, this);

                            log4net.GlobalContext.Properties["assertionId"] = this.Id + " (evaluation " + k + ")";

                            if (precondition)
                            {
                                // Evaluate the XPath expression

                                ProduceOutputFact(formulaProcessor, selector.Evaluate(), cartesianProductItem, variableItems, variableSequences, variableNotSequenceEntries);
                            }
                            else
                            {
                                formulaProcessor.formulaNotSatisfied++;
                            }
                        }
                    }
                }                                
            }

            if (variableSequences.Count > 0 && variableNamesAsItem.Count == 0)
            {
                Dictionary<string, FactItem> variableNotSequenceEntries = new Dictionary<string, FactItem>();

                List<FactItem> cartesianProductItem = new List<FactItem>();

                foreach (string sequenceName in variableSequences.Keys)
                {
                    foreach (List<FactItem> currentSequence in variableSequences[sequenceName])
                    {
                        variableAsSequenceEntries.Clear();

                        variableAsSequenceEntries.Add(sequenceName, currentSequence);

                        // Bind Variable

                        BindVariable(FormulaXBRL.processor, selector, cartesianProductItem, variableNamesAsItem, this.variableAsSequenceEntries, variableNotSequenceEntries, variableFallBack);

                        // Evaluate Precondition

                        Boolean precondition = EvaluatePrecondition(compiler, variableNotSequenceEntries, this);

                        log4net.GlobalContext.Properties["assertionId"] = this.Id + " (evaluation " + k + ")";

                        if (precondition)
                        {
                            // Evaluate the XPath expression

                            ProduceOutputFact(formulaProcessor, selector.Evaluate(), cartesianProductItem, variableItems, variableSequences, variableNotSequenceEntries);
                        }
                        else
                        {
                            formulaProcessor.formulaNotSatisfied++;
                        }
                    }
                }
            }

        }


        internal void EvaluateFactVariables(Dictionary<string, List<FactItem>> variableItems, Dictionary<string, List<List<FactItem>>> variableSequences, Dictionary<string, XdmAtomicValue> variableFallBack)
        {
            List<FactItem> itemsToFilter = GetItems(dts.Instance.Facts);

            foreach (Filter filter in this.GroupFilters)
            {
                itemsToFilter = filter.EvaluateFilter(this, itemsToFilter, dts);
            }            

            AddVertices(dependencyGraph);

            AddEdges(dependencyGraph);

            Queue<Vertex> q = dependencyGraph.GetVerticesInDegreeZero();

            List<Vertex> evaluationOrderVariable = new List<Vertex>();
            List<Vertex> unresolved = new List<Vertex>();

            foreach (Vertex vertex in q)
            {
                dependencyGraph.DependencyResolve(vertex, ref evaluationOrderVariable, ref unresolved);
            }

            foreach (Vertex vertex in evaluationOrderVariable)
            {
                //FactVariable factVariable = this.GetFactVariable(vertex.name, this);

                Variable variable = this.GetVariable(vertex.name, this);

                if (variable is FactVariable)
                {
                    FactVariable factVariable = variable as FactVariable;

                    List<FactItem> factVariableItems = this.FilteringFacts(this, factVariable, itemsToFilter);

                    if (!factVariable.BindAsSequence)
                    {
                        if (factVariableItems.Count != 0)
                        {
                            this.variableItems.Add(vertex.name, factVariableItems);
                        }
                        else
                        {
                            if (factVariable.FallbackString != null)
                            {
                                variableFallBack.Add(vertex.name, new XdmAtomicValue(factVariable.FallbackValue));
                            }
                        }
                    }
                    else
                    {
                        if (this.sequencesConceptRelationFilter.Count == 0)
                        {
                            List<List<FactItem>> sequences = new List<List<FactItem>>();

                            EvaluateSourceSequence(factVariable, factVariableItems, sequences);

                            this.variableSequences.Add(vertex.name, sequences);
                        }
                        else
                        {
                            this.variableSequences.Add(vertex.name, this.sequencesConceptRelationFilter);
                        }
                    }
                }
                else if (variable is GeneralVariable)
                {
                    List<string> dependecyVariables = GetDependecyVariables(this, vertex.name);

                    string result = EvaluateExpression(dependecyVariables, this, (variable as GeneralVariable).Select);
                }
            }            
        }       

        
        private void EvaluateSourceSequence(FactVariable factVariable, List<FactItem> factVariableItems, List<List<FactItem>> sequences)
        {
            List<FactItem> temp = new List<FactItem>(factVariableItems);
  
            foreach (FactItem item in factVariableItems)
            {
                List<FactItem> sequence = new List<FactItem>();

                sequence.Add(item);
                temp.Remove(item);

                foreach (FactItem itemToCompare in factVariableItems)
                {
                    if (item != itemToCompare)
                    {
                        if (MatchUncoveredAspect(item, itemToCompare, this, factVariable, factVariable, factVariableItems))
                        {
                            sequence.Add(itemToCompare);
                            temp.Remove(itemToCompare);
                        }
                    }
                }

                sequences.Add(sequence);
                break;
            }

            if (temp.Count > 0)
            {
                EvaluateSourceSequence(factVariable, temp, sequences);
            }
        }


        internal void BindVariable(Processor processor, XPathSelector selector, List<FactItem> cartesianProductItem, List<string> variableNames, Dictionary<string, List<FactItem>> variableAsSequenceEntries, Dictionary<string, FactItem> variableNotSequenceEntries, Dictionary<string, XdmAtomicValue> variableFallBack)
        {
            // bindAsSequence = false

            for (int i = 0; i < cartesianProductItem.Count; i++)
            {
                FactItem item = cartesianProductItem[i];

                string variableName = variableNames[i];
                string prefix = variableName.GetPrefix();  // Item1 = name of variable

                XdmNode xdmNode = processor.NewDocumentBuilder().Build(item.FactElement.ToXmlNode());

                IEnumerator enumerator = xdmNode.EnumerateAxis(XdmAxis.Child);

                XdmNode elementNode = (XdmNode)enumerator.Current;

                //if (prefix.Length > 0)
                //{
                //    selector.SetVariable(new QName(namespaces[prefix], variableName), elementNode);
                //}
                //else
                //{
                //    selector.SetVariable(new QName(variableName), elementNode);
                //}

                double value;

                if (prefix.Length > 0)
                {
                    if (double.TryParse(item.Value, out value))
                    {
                        selector.SetVariable(new QName(namespaces[prefix], variableName), new XdmAtomicValue(value));
                    }
                    else
                    {
                        selector.SetVariable(new QName(namespaces[prefix], variableName), new XdmAtomicValue(item.Value));
                    }
                }
                else
                {
                    if (double.TryParse(item.Value, out value))
                    {
                        selector.SetVariable(new QName(variableName), new XdmAtomicValue(value));
                    }
                    else
                    {
                        selector.SetVariable(new QName(variableName), new XdmAtomicValue(item.Value));
                    }
                }

                log.Debug("Formula trace: Fact Variable " + variableName + ": bound value fact(" + item.Name + ", " + item.ContextRefName + ", " + item.UnitRefName + ", '" + item.Value + "')");
                variableNotSequenceEntries.Add(variableName, item);
            }

            // Fallback variables

            BindVariableFallback(selector, variableFallBack);

            // bindAsSequence = true

            BindVariableSequence(processor, selector, variableAsSequenceEntries);

            foreach (VariableSet variableSet in this.VariableSetArcs)
            {
                if (variableSet.Variable.Name.LocalName.Equals("generalVariable"))
                {
                    GeneralVariable generalVariable = this.GetGeneralVariable(this, variableSet);

                    string prefix = variableSet.Name.GetPrefix();

                    XPathSelector selector2 = compiler.Compile(generalVariable.Select).Load();

                    BindVariableValue(variableNotSequenceEntries, parameterDictionary, selector2, variableAsSequenceEntries);

                    selector2.ContextItem = selector.ContextItem;

                    if (prefix.Length > 0)
                    {
                        selector2.SetVariable(new QName(namespaces[prefix], variableSet.Name), new XdmAtomicValue(0));
                    }
                    else
                    {
                        selector2.SetVariable(new QName(variableSet.Name), new XdmAtomicValue(0));
                    }

                    XdmValue xdmValue = selector2.Evaluate();

                    if (prefix.Length > 0)
                    {
                        selector.SetVariable(new QName(namespaces[prefix], variableSet.Name), xdmValue);
                    }
                    else
                    {
                        selector.SetVariable(new QName(variableSet.Name), xdmValue);
                    }
                }
            }
        }        


        protected bool EvaluatePrecondition(XPathCompiler compiler, Dictionary<string, FactItem> variableEntries, VariableSet formula)
        {
            XPathSelector selectorPrecondition = null;
            Boolean precondition = true;

            if (formula.Precondition != null)
            {
                string preconditionTest = formula.Precondition.Test;
                selectorPrecondition = compiler.Compile(preconditionTest).Load();

                foreach (string variableName in variableEntries.Keys)
                {
                    double value;

                    if (double.TryParse(variableEntries[variableName].Value, out value))
                    {
                        selectorPrecondition.SetVariable(new QName(nsvariable, variableName), new XdmAtomicValue(value));
                    }
                    else
                    {
                        selectorPrecondition.SetVariable(new QName(nsvariable, variableName), new XdmAtomicValue(variableEntries[variableName].Value));
                    }
                }

                precondition = Boolean.Parse(selectorPrecondition.EvaluateSingle().ToString());
            }

            return precondition;
        }


        internal void ProduceOutputFact(FormulaProcessor formulaProcessor, XdmValue xdmValue, List<FactItem> cartesianProductItem, Dictionary<string, List<FactItem>> variableItems, Dictionary<string, List<List<FactItem>>> variableSequences, Dictionary<string, FactItem> variableEntries)
        {
            if (this.Source != null && !this.Source.GetLocalName().Equals("uncovered"))
            {
                FactVariable factVariableSource = this.GetFactVariable(this.Source, this);

                if (factVariableSource.BindAsSequence)
                {
                    log.Error("Formula error: The formula: " + this.Label + " . The source contains the QName of a fact variable that binds as a sequence.");
                    dts.ValidatedFormula = false;
                    return;
                }
            }

            formulaProcessor.formulaSatisfied++;

            XElement context = new XElement(xbrli + "context", new XAttribute("id", "context"));

            FactItem defaultFact = GetDefaultFact(ref context, variableItems, variableSequences, variableEntries);

            if (!BuildOutputContext(ref context, variableItems, variableSequences, variableEntries, defaultFact))
            {
                return;
            }

            // Unit rules

            XElement unit = null;

            if (!BuildOutputUnit(ref unit, variableItems, variableSequences, variableEntries, defaultFact))
            {
                return;
            }            

            // Item Aspect rules                

            XElement itemSAV = null;

            if (!BuildOutputFact(formulaProcessor, xdmValue, ref itemSAV, cartesianProductItem, variableItems, variableSequences, variableEntries, defaultFact))
            {
                return;
            }  

            itemSAV.Add(new XAttribute("contextRef", context.GetAttributeValue("id")));

            if (unit != null)
            {
                itemSAV.Add(new XAttribute("unitRef", unit.GetAttributeValue("id")));
            }

            //xbrl.SetAttributeValue(XNamespace.Xmlns + "concept", itemSAV.Name.Namespace);


            formulaProcessor.ItemsFormula.Add(itemSAV);
        }


        internal bool BuildOutputContext(ref XElement context, Dictionary<string, List<FactItem>> variableItems, Dictionary<string, List<List<FactItem>>> variableSequences, Dictionary<string, FactItem> variableEntries, FactItem defaultFact)
        {
            // Entity identifier rules

            if (!BuildEntityIdentifier(ref context, variableItems, variableSequences, variableEntries, ref defaultFact))
            {
                return false;
            }


            // Period rules

            if (!BuildPeriod(ref context, variableItems, variableSequences, variableEntries, ref defaultFact))
            {
                return false;
            }


            // Explicit dimension rules

            XElement container = null;
            Concept concept = null;

            string contextElement = "scenario";

            if (this.ConceptAspectFormula != null)
            {
                if (this.ConceptAspectFormula.Qname != null)
                {
                    concept = dts.GetConceptByName(ConceptAspectFormula.Qname.LocalName);
                }
                else if (this.ConceptAspectFormula.QnameExpression != null)
                {
                    string result = EvaluateXPathExpression(this, this.ConceptAspectFormula.QnameExpression, variableEntries);

                    concept = dts.GetConceptByName(result.GetLocalName());
                }                
            }

            if (concept == null)
            {
                concept = defaultFact.Concept;
            }

            foreach (DimensionalRelationshipSet drs in dts.DtsDimension.DimensionalRelationshipSets)
            {
                DimensionalRelationshipSet drsTotal = new DimensionalRelationshipSet();

                drsTotal.DRSRole = drs.DRSRole;
                List<ConsecutiveRelationshipsSet> crss = GetConsecutiveRelationshipsSets(concept, drs.DRSRole);
                drsTotal.ConsecutiveRelationshipsSets.AddRange(crss);

                List<ConsecutiveRelationshipsSet> crsInherited = GetCRSInherited(concept, drs.DRSRole);
                drsTotal.ConsecutiveRelationshipsSets.AddRange(crsInherited);

                if (drsTotal.ConsecutiveRelationshipsSets.Count > 0)
                {
                    contextElement = drsTotal.ConsecutiveRelationshipsSets[0].HasHypercube.ContextElement;
                }
            }


            container = new XElement(xbrli + contextElement);

            if (contextElement == "scenario")
            {
                UpdateContainerSegment(context, ref container);
            }
            else
            {
                UpdateContainerScenario(context, ref container);
            }

            if (!BuildExplicitDimension(context, variableItems, variableSequences, variableEntries, defaultFact, container))
            {
                return false;
            }


            // Typed dimension rules

            if (!BuildTypedDimension(context, variableItems, variableSequences, variableEntries, defaultFact, container))
            {
                return false;
            }


            // Open context component rules

            BuildOCC(context, variableEntries);

            FormulaProcessor.xbrl.Add(context);

            return true;
        }


        private FactItem GetDefaultFact(ref XElement context, Dictionary<string, List<FactItem>> variableItems, Dictionary<string, List<List<FactItem>>> variableSequences, Dictionary<string, FactItem> variableEntries)
        {
            FactItem defaultFact = null;

            if (variableSequences.Count > 0 && variableItems.Count == 0)
            {
                return variableSequences.Values.ElementAt(0).ElementAt(0).FirstOrDefault();
            }

            if (this.Source != null && !this.Source.GetLocalName().Equals("uncovered"))
            {
                defaultFact = variableEntries[this.Source];
                //context = new XElement(defaultFact.ContextRef.ContextElement);
            }
            else if (this.Source != null && this.Source.GetLocalName().Equals("uncovered"))
            {
                if (this.ConceptAspectFormula != null)
                {
                    defaultFact = variableEntries[variableItems.ElementAt(0).Key];
                    //context = new XElement(defaultFact.ContextRef.ContextElement);
                }
                else
                {
                    for (int i = 0; i < variableItems.Count; i++)
                    {
                        FactVariable factVariable = this.GetFactVariable(variableItems.ElementAt(i).Key, this);

                        if (!this.CoveredAspects.Contains("conceptFilter"))
                        {
                            defaultFact = variableEntries[variableItems.ElementAt(i).Key];
                            //context = new XElement(defaultFact.ContextRef.ContextElement);
                            break;
                        }
                    }
                }
            }

            return defaultFact;
        }


        private bool BuildEntityIdentifier(ref XElement context, Dictionary<string, List<FactItem>> variableItems, Dictionary<string, List<List<FactItem>>> variableSequences, Dictionary<string, FactItem> variableEntries, ref FactItem defaultFact)
        {
            XElement contextSAV = defaultFact.ContextRef.ContextElement;

            if (this.EntityIdentifierAspectFormula != null)
            {
                XElement newEntity = defaultFact.ContextRef.EntityElement;

                if (EntityIdentifierAspectFormula.Source != null && !EntityIdentifierAspectFormula.Source.GetLocalName().Equals("uncovered"))
                {
                    if (variableEntries.Count > 0)
                    {
                        defaultFact = variableEntries[EntityIdentifierAspectFormula.Source];                        
                    }

                    contextSAV = new XElement(defaultFact.ContextRef.ContextElement);
                    newEntity = contextSAV.Element(xbrli + "entity");

                }

                if (EntityIdentifierAspectFormula.Source != null && EntityIdentifierAspectFormula.Source.GetLocalName().Equals("uncovered"))
                {
                    bool found = false;

                    for (int i = 0; i < variableItems.Count; i++)
                    {
                        FactVariable factVariable = this.GetFactVariable(variableItems.ElementAt(i).Key, this);

                        if (!factVariable.CoveredAspects.Contains("identifier") &&
                            !factVariable.CoveredAspects.Contains("specificIdentifier"))
                        {
                            if (variableEntries.Count > 0)
                            {
                                defaultFact = variableEntries[variableItems.ElementAt(i).Key];                                
                            }

                            contextSAV = new XElement(defaultFact.ContextRef.ContextElement);
                            newEntity = contextSAV.Element(xbrli + "entity");
                            found = true;
                            break;
                        }
                    }

                    if (!found)
                    {
                        log.Error("Formula error: The formula: " + this.Label + " . The source does not imply a SAV for an aspect rule that depends on it.");
                        dts.ValidatedFormula = false;
                        return false;
                    }
                }

                if (defaultFact == null && !EntityIdentifierAspectFormula.Element.HasElements)
                {
                    log.Error("Formula error: The formula: " + this.Label + " .The entity identifier rule does not have a nearest source and does not have either a @scheme or a @value attribute.");
                    dts.ValidatedFormula = false;
                    return false;
                }

                // The required aspect value (RAV) 

                if (this.EntityIdentifierAspectFormula.Scheme != null)
                {
                    string result = EvaluateXPathExpression(this, this.EntityIdentifierAspectFormula.Scheme, variableEntries);

                    newEntity.Element(xbrli + "identifier").SetAttributeValue("scheme", result);
                }

                if (this.EntityIdentifierAspectFormula.ValueScheme != null)
                {
                    string result = EvaluateXPathExpression(this, this.EntityIdentifierAspectFormula.ValueScheme, variableEntries);

                    newEntity.SetElementValue(xbrli + "identifier", result);
                }

                XElement oldEntity = contextSAV.Element(xbrli + "entity");

                if (newEntity != null)
                {
                    context.Add(newEntity);
                }               
            }
            else
            {
                if (this.Source != null && !this.Source.GetLocalName().Equals("uncovered"))
                {
                    if (variableEntries.Count > 0)
                    {
                        defaultFact = variableEntries[this.Source];                        
                    }

                    contextSAV = new XElement(defaultFact.ContextRef.ContextElement);
                    XElement oldEntity = contextSAV.Element(xbrli + "entity");
                    oldEntity.Elements(xbrli + "segment").Remove();
                    context.Add(oldEntity);
                }

                if (this.Source != null && this.Source.GetLocalName().Equals("uncovered"))
                {
                    bool found = false;

                    for (int i = 0; i < variableItems.Count; i++)
                    {
                        FactVariable factVariable = this.GetFactVariable(variableItems.ElementAt(i).Key, this);

                        if (!factVariable.CoveredAspects.Contains("identifier"))
                        {
                            if (variableEntries.Count > 0)
                            {
                                defaultFact = variableEntries[variableItems.ElementAt(i).Key];                                
                            }

                            contextSAV = new XElement(defaultFact.ContextRef.ContextElement);
                            XElement oldEntity = contextSAV.Element(xbrli + "entity");
                            oldEntity.Elements(xbrli + "segment").Remove();
                            context.Add(oldEntity);
                            found = true;
                            break;
                        }
                    }

                    if (!found)
                    {
                        log.Error("Formula error: The formula: " + this.Label + " . The source does not imply a SAV for an aspect rule that depends on it.");
                        dts.ValidatedFormula = false;
                        return false;
                    }
                }
            }

            return true;
        }


        private bool BuildPeriod(ref XElement context, Dictionary<string, List<FactItem>> variableItems, Dictionary<string, List<List<FactItem>>> variableSequences, Dictionary<string, FactItem> variableEntries, ref FactItem defaultFact)
        {
            XElement contextSAV = null;

            if (this.PeriodAspectFormula != null)
            {
                XElement newPeriod = new XElement(xbrli + "period");

                if (PeriodAspectFormula.Source != null && !PeriodAspectFormula.Source.GetLocalName().Equals("uncovered"))
                {
                    if (variableEntries.Count > 0)
                    {
                        defaultFact = variableEntries[PeriodAspectFormula.Source];                        
                    }

                    contextSAV = new XElement(defaultFact.ContextRef.ContextElement);
                    newPeriod = new XElement(contextSAV.Element(xbrli + "period"));
                }

                if (PeriodAspectFormula.Source != null && PeriodAspectFormula.Source.GetLocalName().Equals("uncovered"))
                {
                    // tuple(Name, FallbackValue, BindAsSequence)
                    for (int i = 0; i < variableItems.Count; i++)
                    {
                        FactVariable factVariable = this.GetFactVariable(variableItems.ElementAt(i).Key, this);

                        if (!factVariable.CoveredAspects.Contains("period"))
                        {
                            if (variableEntries.Count > 0)
                            {
                                defaultFact = variableEntries[variableItems.ElementAt(i).Key];                                
                            }

                            contextSAV = new XElement(defaultFact.ContextRef.ContextElement);
                            newPeriod = new XElement(contextSAV.Element(xbrli + "period"));
                        }
                    }
                }

                if (defaultFact == null && !PeriodAspectFormula.Element.HasElements)
                {
                    log.Error("Formula error: The formula: " + this.Label + " . The period rule does not have a nearest source and does not have a child element.");
                    dts.ValidatedFormula = false;
                    return false;
                }

                // The required aspect value (RAV) 

                if (this.PeriodAspectFormula.Forever != null)
                {
                    newPeriod.Elements().Remove();
                    newPeriod.DescendantNodes().OfType<XText>().Remove();

                    newPeriod.Add(new XElement(xbrli + "forever"));

                    context.SetAttributeValue("id", "forever");
                }
                else if (this.PeriodAspectFormula.Instant != null)
                {
                    string result = EvaluateXPathExpression(this, this.PeriodAspectFormula.Instant, variableEntries);

                    newPeriod.Elements().Remove();
                    newPeriod.Add(new XElement(xbrli + "instant", result));

                    int year = DateTime.Parse(result).Year;
                    context.SetAttributeValue("id", "c" + k);
                }
                else if (this.PeriodAspectFormula.DurationStart != null)
                {
                    string start = EvaluateXPathExpression(this, this.PeriodAspectFormula.DurationStart, variableEntries);
                    string end = EvaluateXPathExpression(this, this.PeriodAspectFormula.DurationStart, variableEntries);

                    newPeriod.Elements().Remove();

                    newPeriod.Add(new XElement(xbrli + "startDate", start));
                    newPeriod.Add(new XElement(xbrli + "endDate", end));

                    int yearS = DateTime.Parse(start).Year;
                    int yearE = DateTime.Parse(start).Year;
                    context.SetAttributeValue("id", "I" + yearS + "-" + yearE);
                }

                XElement oldPeriod = context.Element(xbrli + "period");

                if (newPeriod != null)
                {
                    context.Add(newPeriod);
                }                
            }
            else
            {
                if (this.Source != null && !this.Source.GetLocalName().Equals("uncovered"))
                {
                    if (variableEntries.Count > 0)
                    {
                        defaultFact = variableEntries[this.Source];                       
                    }

                    contextSAV = new XElement(defaultFact.ContextRef.ContextElement);
                    XElement oldPeriod = contextSAV.Element(xbrli + "period");
                    oldPeriod.Elements(xbrli + "segment").Remove();
                    context.Add(oldPeriod);
                }

                if (this.Source != null && this.Source.GetLocalName().Equals("uncovered"))
                {
                    bool found = false;

                    for (int i = 0; i < variableItems.Count; i++)
                    {
                        FactVariable factVariable = this.GetFactVariable(variableItems.ElementAt(i).Key, this);

                        if (!factVariable.CoveredAspects.Contains("period"))
                        {
                            if (variableEntries.Count > 0)
                            {
                                defaultFact = variableEntries[variableItems.ElementAt(i).Key];                                
                            }

                            contextSAV = new XElement(defaultFact.ContextRef.ContextElement);
                            XElement oldPeriod = contextSAV.Element(xbrli + "period");
                            oldPeriod.Elements(xbrli + "segment").Remove();
                            context.Add(oldPeriod);
                            found = true;
                            break;
                        }
                    }

                    if (!found)
                    {
                        log.Error("Formula error: The formula: " + this.Label + " . The source does not imply a SAV for an aspect rule that depends on it.");
                        dts.ValidatedFormula = false;
                        return false;
                    }
                }
            }

            k = k + 1;
            return true;
        }


        private bool BuildExplicitDimension(XElement context, Dictionary<string, List<FactItem>> variableItems, Dictionary<string, List<List<FactItem>>> variableSequences, Dictionary<string, FactItem> variableEntries, FactItem defaultFact, XElement container)
        {
            XElement contextSAV = null;

            foreach (ExplicitDimensionAspect explicitDimensionAspect in ExplicitDimensionAspectFormulas)
            {
                if (explicitDimensionAspect.Omit != null)
                {
                    RemoveDimInSegment(context, explicitDimensionAspect);

                    RemoveDimInScenario(context, explicitDimensionAspect);
                }

                XElement explicitDimensionSAV = null;

                if (explicitDimensionAspect.Source != null && !explicitDimensionAspect.Source.GetLocalName().Equals("uncovered"))
                {
                    if (variableEntries.Count > 0)
                    {
                        defaultFact = variableEntries[explicitDimensionAspect.Source];                        
                    }

                    contextSAV = new XElement(defaultFact.ContextRef.ContextElement);

                    explicitDimensionSAV = contextSAV.Descendants(xbrldi + "explicitMember").
                       Where(e => e.Attribute("dimension").Value == explicitDimensionAspect.Dimension).FirstOrDefault();
                }

                if (explicitDimensionAspect.Source != null && explicitDimensionAspect.Source.GetLocalName().Equals("uncovered"))
                {
                    // tuple(Name, FallbackValue, BindAsSequence)
                    for (int i = 0; i < variableItems.Count; i++)
                    {
                        FactVariable factVariable = this.GetFactVariable(variableItems.ElementAt(i).Key, this);

                        if (!factVariable.CoveredAspects.Contains("dimension"))
                        {
                            if (variableEntries.Count > 0)
                            {
                                defaultFact = variableEntries[variableItems.ElementAt(i).Key];                                
                            }

                            contextSAV = new XElement(defaultFact.ContextRef.ContextElement);

                            explicitDimensionSAV = contextSAV.Descendants(xbrldi + "explicitMember").
                             Where(e => e.Attribute("dimension").Value == explicitDimensionAspect.Dimension).FirstOrDefault();
                        }
                    }
                }

                if (defaultFact == null && !explicitDimensionAspect.Element.HasElements)
                {
                    log.Error("Formula error: The formula: " + this.Label + " . The explicit dimension rule does not have any child elements and does not have a SAV for the dimension that identified by its @dimension attribute.");
                    dts.ValidatedFormula = false;
                    return false;
                }

                if (explicitDimensionAspect.Member != null)
                {
                    string member = EvaluateXPathExpression(this, explicitDimensionAspect.Member, variableEntries);

                    if (explicitDimensionSAV != null)
                    {
                        explicitDimensionSAV.Value = member;
                    }
                    else
                    {
                        //XElement container2 = contextSAV.Descendants(container.Name).FirstOrDefault();
                        container.Add(new XElement(xbrldi + "explicitMember", member,
                            new XAttribute("dimension", explicitDimensionAspect.Dimension)));
                    }
                }               

                if (!explicitDimensionAspect.Element.HasElements)
                {
                    if (explicitDimensionSAV != null)
                    {
                        //XElement container2 = new XEcontext.Descendants(container.Name).FirstOrDefault();
                        container.Add(explicitDimensionSAV);
                    }
                }                
            }

            if (ExplicitDimensionAspectFormulas.Count == 0)
            {
                if (this.Source != null && !this.Source.GetLocalName().Equals("uncovered"))
                {
                    if (variableEntries.Count > 0)
                    {
                        defaultFact = variableEntries[this.Source];                        
                    }

                    contextSAV = new XElement(defaultFact.ContextRef.ContextElement);

                    IEnumerable<XElement> explicitDimensionSAV = contextSAV.Descendants(xbrldi + "explicitMember");
                    container.Add(explicitDimensionSAV);
                }                
            }

            if (this.Source != null && this.Source.GetLocalName().Equals("uncovered"))
            {
                for (int i = 0; i < variableItems.Count; i++)
                {
                    FactVariable factVariable = this.GetFactVariable(variableItems.ElementAt(i).Key, this);
                    defaultFact = variableEntries[variableItems.ElementAt(i).Key];
                    contextSAV = new XElement(defaultFact.ContextRef.ContextElement);
                    IEnumerable<XElement> explicitDimensionSAV = contextSAV.Descendants(xbrldi + "explicitMember");
                    bool isCovered = false;

                    foreach (XElement explicitDimension in explicitDimensionSAV)
                    {
                        string dimension = explicitDimension.Attribute("dimension").Value;

                        if (ExistExplicitDimensionAspectRule(dimension))
                        {
                            continue;
                        }

                        isCovered = false;

                        foreach (Filter filter in factVariable.Filters)
                        {
                            if (filter.ResourceName == "explicitDimension")
                            {
                                if (dimension == (filter as ExplicitDimensionFilter).Dimension)
                                {
                                    isCovered = true;
                                }
                            }
                        }

                        if (!isCovered)
                        {
                            container.Add(explicitDimension);
                            break;
                        }
                    }
                }
            }

            return true;
        }


        private bool ExistExplicitDimensionAspectRule(string dimension)
        {
            foreach (ExplicitDimensionAspect explicitDimensionAspect in ExplicitDimensionAspectFormulas)
            {
                if (explicitDimensionAspect.Dimension == dimension)
                {
                    return true;
                }
            }

            return false;
        }


        private bool BuildTypedDimension(XElement context, Dictionary<string, List<FactItem>> variableItems, Dictionary<string, List<List<FactItem>>> variableSequences, Dictionary<string, FactItem> variableEntries, FactItem defaultFact, XElement container)
        {
            XElement contextSAV = null;
            XElement typedDimensionSAV = null;

            foreach (TypedDimensionAspect typedDimensionAspect in TypedDimensionAspectFormulas)
            {
                if (typedDimensionAspect.Omit != null)
                {
                    XElement segment = context.Descendants(xbrli + "segment").FirstOrDefault();

                    if (segment != null)
                    {
                        segment.Descendants(xbrldi + "typedMember").Where(e => e.Attribute("dimension").Value == typedDimensionAspect.Dimension).Remove();
                        segment.DescendantNodes().OfType<XText>().Remove();

                        if (!segment.HasElements)
                        {
                            context.Descendants(xbrli + "segment").Remove();
                            //context.DescendantNodes().OfType<XText>().Remove();
                        }
                    }

                    XElement scenario = context.Descendants(xbrli + "scenario").FirstOrDefault();

                    if (scenario != null)
                    {
                        scenario.Descendants(xbrldi + "typedMember").Where(e => e.Attribute("dimension").Value == typedDimensionAspect.Dimension).Remove();
                        scenario.DescendantNodes().OfType<XText>().Remove();

                        if (!scenario.HasElements)
                        {
                            context.Descendants(xbrli + "scenario").Remove();
                            //context.DescendantNodes().OfType<XText>().Remove();
                        }
                    }
                }

                if (typedDimensionAspect.Source != null && !typedDimensionAspect.Source.GetLocalName().Equals("uncovered"))
                {
                    if (variableEntries.Count > 0)
                    {
                        defaultFact = variableEntries[typedDimensionAspect.Source];
                    }

                    contextSAV = new XElement(defaultFact.ContextRef.ContextElement);

                    typedDimensionSAV = contextSAV.Descendants(xbrldi + "typedMember")
                       .Where(e => e.Attribute("dimension").Value == typedDimensionAspect.Dimension).FirstOrDefault();
                }

                if (typedDimensionAspect.Source != null && typedDimensionAspect.Source.GetLocalName().Equals("uncovered"))
                {
                    // tuple(Name, FallbackValue, BindAsSequence)
                    for (int i = 0; i < variableItems.Count; i++)
                    {
                        FactVariable factVariable = this.GetFactVariable(variableItems.ElementAt(i).Key, this);

                        if (!factVariable.CoveredAspects.Contains("dimension"))
                        {
                            if (variableEntries.Count > 0)
                            {
                                defaultFact = variableEntries[variableItems.ElementAt(i).Key];
                            }

                            contextSAV = new XElement(defaultFact.ContextRef.ContextElement);

                            typedDimensionSAV = contextSAV.Descendants(xbrldi + "typedMember")
                                .Where(e => e.Attribute("dimension").Value == typedDimensionAspect.Dimension).FirstOrDefault();
                        }
                    }
                }

                if (defaultFact == null && !typedDimensionAspect.Element.HasElements)
                {
                    log.Error("Formula error: The formula: " + this.Label + " . The typed dimension rule does not have any child elements and does not have a SAV for the dimension that identified by its @dimension attribute.");
                    dts.ValidatedFormula = false;
                    return false;
                }

                if (typedDimensionAspect.Value != null)
                {
                    // <xbrldi:typedMember dimension="company:CustomerDim"><company:CustomerCode>ABC2</company:CustomerCode></xbrldi:typedMember>
                    container.Add(new XElement(xbrldi + "typedMember", typedDimensionAspect.Value,
                        new XAttribute("dimension", typedDimensionAspect.Dimension)));

                   // context.Add(container);
                }
                else if (typedDimensionAspect.Xpath != null)
                {
                    string xpath = EvaluateXPathExpression(this, typedDimensionAspect.Xpath, variableEntries);
                    container.Add(new XElement(xbrldi + "typedMember", xpath,
                        new XAttribute("dimension", typedDimensionAspect.Dimension)));

                    //context.Add(container);
                }

                if (!typedDimensionAspect.Element.HasElements)
                {
                    if (typedDimensionSAV != null)
                    {
                        container.Add(typedDimensionSAV);
                    }
                }
            }

            if (TypedDimensionAspectFormulas.Count == 0)
            {
                if (this.Source != null && !this.Source.GetLocalName().Equals("uncovered"))
                {
                    if (variableEntries.Count > 0)
                    {
                        defaultFact = variableEntries[this.Source];
                    }

                    contextSAV = new XElement(defaultFact.ContextRef.ContextElement);

                    IEnumerable<XElement> typedDimensions = contextSAV.Descendants(xbrldi + "typedMember");
                    container.Add(typedDimensions);
                }                
            }

            if (this.Source != null && this.Source.GetLocalName().Equals("uncovered"))
            {
                for (int i = 0; i < variableItems.Count; i++)
                {
                    FactVariable factVariable = this.GetFactVariable(variableItems.ElementAt(i).Key, this);

                    if (variableEntries.Count > 0)
                    {
                        defaultFact = variableEntries[variableItems.ElementAt(i).Key];
                    }

                    contextSAV = new XElement(defaultFact.ContextRef.ContextElement);
                    IEnumerable<XElement> typedDimensions = contextSAV.Descendants(xbrldi + "typedMember");
                    bool isCovered = false;

                    foreach (XElement typedDimension in typedDimensions)
                    {
                        string dimension = typedDimension.Attribute("dimension").Value;

                        if (ExistTypedDimensionAspectRule(dimension))
                        {
                            continue;
                        }

                        isCovered = false;

                        foreach (Filter filter in factVariable.Filters)
                        {
                            if (filter.ResourceName == "typedDimension")
                            {
                                if (dimension == (filter as TypedDimensionFilter).Dimension)
                                {
                                    isCovered = true;
                                }
                            }
                        }

                        if (!isCovered)
                        {
                            container.Add(typedDimension);
                            break;
                        }
                    }
                }
            }

            if (container.Name.LocalName == "scenario")
            {
                context.Add(container);
            }
            else
            {
                context.Element(xbrli + "entity").Add(container);
            }

            return true;
        }


        private bool ExistTypedDimensionAspectRule(string dimension)
        {
            foreach (TypedDimensionAspect typedDimensionAspect in TypedDimensionAspectFormulas)
            {
                if (typedDimensionAspect.Dimension == dimension)
                {
                    return true;
                }
            }

            return false;
        }


        private void BuildOCC(XElement context, Dictionary<string, FactItem> variableEntries)
        {
            // Empty OCC rules

            XElement segmentParent = null;
            XElement scenarioParent = null;

            if (context.Descendants(xbrli + "segment").FirstOrDefault() != null)
            {
                segmentParent = context.Descendants(xbrli + "segment").FirstOrDefault().Parent;
            }

            if (context.Descendants(xbrli + "scenario").FirstOrDefault() != null)
            {
                scenarioParent = context.Descendants(xbrli + "scenario").FirstOrDefault().Parent;
            }

            if (this.OccEmptyAspectFormula != null)
            {
                if (this.OccEmptyAspectFormula.Occ.Equals("segment"))
                {
                    context.Descendants(xbrli + "segment").Remove();
                    context.DescendantNodes().OfType<XText>().Remove();
                }

                if (this.OccEmptyAspectFormula.Occ.Equals("scenario"))
                {
                    context.Descendants(xbrli + "scenario").Remove();
                    context.DescendantNodes().OfType<XText>().Remove();
                }
            }

            // Fragment OCC rules
            if (this.OccFragmentsAspectFormula != null)
            {
                if (this.AspectModel == "dimensional")
                {
                    if (OccFragmentsAspectFormula.Element.Descendants(xbrldi + "explicitMember").Count() > 0 ||
                        OccFragmentsAspectFormula.Element.Descendants(xbrldi + "typedMember").Count() > 0)
                    {
                        log.Error("Formula error: The formula: " + this.Label + " . The bad Subsequent OCCValue");
                        dts.ValidatedFormula = false;
                        return;
                    }
                }

                if (this.OccFragmentsAspectFormula.Occ.Equals("segment"))
                {
                    XElement segment = context.Descendants(xbrli + "segment").FirstOrDefault();

                    if (segment != null)
                    {
                        segment.Add(this.OccFragmentsAspectFormula.Element.Elements());
                    }
                    else
                    {
                        segment = new XElement(xbrli + "segment");
                        segment.Add(this.OccFragmentsAspectFormula.Element.Elements());
                        segmentParent.Add(segment);
                    }
                }

                if (this.OccFragmentsAspectFormula.Occ.Equals("scenario"))
                {
                    XElement scenario = context.Descendants(xbrli + "scenario").FirstOrDefault();

                    if (scenario != null)
                    {
                        scenario.Add(this.OccFragmentsAspectFormula.Element.Elements());
                    }
                    else
                    {
                        scenario = new XElement(xbrli + "scenario");
                        scenario.Add(this.OccFragmentsAspectFormula.Element.Elements());
                        scenarioParent.Add(scenario);
                    }
                }
            }

            // XPath OCC rules

            if (this.OccXpathAspectFormula != null)
            {
                string result = EvaluateXPathExpression(this, this.OccXpathAspectFormula.Select, variableEntries);

                XElement newItem = XElement.Parse(result);

                if (this.OccXpathAspectFormula.Occ.Equals("segment"))
                {
                    XElement segment = context.Descendants(xbrli + "segment").FirstOrDefault();

                    if (segment != null)
                    {
                        segment.Add(newItem);
                    }
                    else
                    {
                        segment = new XElement(xbrli + "segment");
                        segment.Add(newItem);
                        segmentParent.Add(segment);
                    }
                }

                if (this.OccXpathAspectFormula.Occ.Equals("scenario"))
                {
                    XElement scenario = context.Descendants(xbrli + "scenario").FirstOrDefault();

                    if (scenario != null)
                    {
                        scenario.Add(newItem);
                    }
                    else
                    {
                        scenario = new XElement(xbrli + "scenario");
                        scenario.Add(newItem);
                        scenarioParent.Add(scenario);
                    }
                }
            }
        }


        private void RemoveDimInSegment(XElement context, ExplicitDimensionAspect explicitDimensionAspect)
        {
            XElement segment = context.Descendants(xbrli + "segment").FirstOrDefault();

            if (segment != null)
            {
                segment.Descendants(xbrldi + "explicitMember").Where(e => e.Attribute("dimension").Value == explicitDimensionAspect.Dimension).Remove();
                segment.DescendantNodes().OfType<XText>().Remove();

                if (!segment.HasElements)
                {
                    context.Descendants(xbrli + "segment").Remove();
                    //context.DescendantNodes().OfType<XText>().Remove();
                }
            }
        }


        private void UpdateContainerSegment(XElement context, ref XElement container)
        {
            XElement segment = context.Descendants(xbrli + "segment").FirstOrDefault();

            if (segment != null)
            {
                if (segment.Descendants(xbrldi + "explicitMember").Count() > 0)                
                {
                    IEnumerable<XElement> explicitMembers = segment.Descendants(xbrldi + "explicitMember");
                    container.Add(explicitMembers.FirstOrDefault());
                    context.Descendants(xbrli + "segment").Descendants(xbrldi + "explicitMember").Remove();
                    context.Descendants(xbrli + "segment").DescendantNodes().OfType<XText>().Remove();
                }                

                if (segment.Descendants(xbrldi + "typedMember").Count() > 0)                
                {
                    IEnumerable<XElement> typedMembers = segment.Descendants(xbrldi + "typedMember");
                    container.Add(typedMembers.FirstOrDefault());
                    context.Descendants(xbrli + "segment").Descendants(xbrldi + "typedMember").Remove();
                    context.Descendants(xbrli + "segment").DescendantNodes().OfType<XText>().Remove();
                }      

                if (!segment.HasElements)
                {
                    context.Descendants(xbrli + "segment").Remove();
                }
            }
        }


        private void RemoveDimInScenario(XElement context, ExplicitDimensionAspect explicitDimensionAspect)
        {
            XElement scenario = context.Descendants(xbrli + "scenario").FirstOrDefault();

            if (scenario != null)
            {
                scenario.Descendants(xbrldi + "explicitMember").Where(e => e.Attribute("dimension").Value == explicitDimensionAspect.Dimension).Remove();
                scenario.DescendantNodes().OfType<XText>().Remove();

                if (!scenario.HasElements)
                {
                    context.Descendants(xbrli + "scenario").Remove();
                    //context.DescendantNodes().OfType<XText>().Remove();
                }
            }
        }


        private void UpdateContainerScenario(XElement context, ref XElement container)
        {
            XElement scenario = context.Descendants(xbrli + "scenario").FirstOrDefault();

            if (scenario != null)
            {
                if (scenario.Descendants(xbrldi + "explicitMember").Count() > 0)
                {
                    IEnumerable<XElement> explicitMembers = scenario.Descendants(xbrldi + "explicitMember");
                    container.Add(explicitMembers);
                    scenario.Descendants(xbrldi + "explicitMember").Remove();
                    scenario.DescendantNodes().OfType<XText>().Remove();
                }

                if (scenario.Descendants(xbrldi + "typedMember").Count() > 0)
                {
                    IEnumerable<XElement> typedMembers = scenario.Descendants(xbrldi + "typedMember");
                    container.Add(typedMembers);
                    scenario.Descendants(xbrldi + "typedMember").Remove();
                    scenario.DescendantNodes().OfType<XText>().Remove();
                }

                if (!scenario.HasElements)
                {
                    context.Descendants(xbrli + "scenario").Remove();
                }
            }
        }


        internal bool BuildOutputUnit(ref XElement unit, Dictionary<string, List<FactItem>> variableItems, Dictionary<string, List<List<FactItem>>> variableSequences, Dictionary<string, FactItem> variableEntries, FactItem defaultFact)
        {
            //FactItem defaultFact = null;

            if (this.Source != null && !this.Source.GetLocalName().Equals("uncovered"))
            {
                if (variableEntries.Count > 0)
                {
                    defaultFact = variableEntries[this.Source];                    
                }

                if (defaultFact.UnitRef != null)
                {
                    unit = new XElement(defaultFact.UnitRef.UnitElement);
                }
            }
            else
            {
                if (this.ConceptAspectFormula != null)
                {
                    if (variableEntries.Count > 0)
                    {
                        defaultFact = variableEntries[variableItems.ElementAt(0).Key];
                    }

                    if (defaultFact.UnitRef != null)
                    {
                        unit = new XElement(defaultFact.UnitRef.UnitElement);
                    }
                }
                else
                {
                    for (int i = 0; i < variableItems.Count; i++)
                    {
                        FactVariable factVariable = this.GetFactVariable(variableItems.ElementAt(i).Key, this);

                        if (!this.CoveredAspects.Contains("conceptFilter"))
                        {
                            if (variableEntries.Count > 0)
                            {
                                defaultFact = variableEntries[variableItems.ElementAt(i).Key];
                            }

                            if (defaultFact.UnitRef != null)
                            {
                                unit = new XElement(defaultFact.UnitRef.UnitElement);
                            }

                            break;
                        }
                    }
                }
            }

            if (UnitAspectFormula != null)
            {
                if (UnitAspectFormula.Augment)
                {
                    if (UnitAspectFormula.Source != null && !UnitAspectFormula.Source.GetLocalName().Equals("uncovered"))
                    {
                        if (variableEntries.Count > 0)
                        {
                            defaultFact = variableEntries[UnitAspectFormula.Source];

                        }

                        if (defaultFact.UnitRef != null)
                        {
                            unit = new XElement(defaultFact.UnitRef.UnitElement);
                        }
                    }

                    if (UnitAspectFormula.Source != null && UnitAspectFormula.Source.GetLocalName().Equals("uncovered"))
                    {
                        // tuple(Name, FallbackValue, BindAsSequence)
                        for (int i = 0; i < variableItems.Count; i++)
                        {
                            FactVariable factVariable = this.GetFactVariable(variableItems.ElementAt(i).Key, this);

                            if (!factVariable.CoveredAspects.Contains("generalMeasures") &&
                                !factVariable.CoveredAspects.Contains("singleMeasure"))
                            {
                                if (variableEntries.Count > 0)
                                {
                                    defaultFact = variableEntries[variableItems.ElementAt(i).Key];
                                }

                                if (defaultFact.UnitRef != null)
                                {
                                    unit = new XElement(defaultFact.UnitRef.UnitElement);
                                }
                            }
                        }
                    }
                }
                else // Augment = false
                {
                    //defaultFact = null;
                }

                foreach (MultiplyByAspect multiplyByAspect in UnitAspectFormula.MultiplyByAspectFormulas)
                {
                    // Unit multiplication rules

                    if (multiplyByAspect.Source != null)
                    {
                        if (variableEntries.Count > 0)
                        {
                            defaultFact = variableEntries[multiplyByAspect.Source];
                        }

                        if (defaultFact.UnitRef != null) // item is numeric
                        {
                            unit = new XElement(defaultFact.UnitRef.UnitElement);
                        }
                    }                   

                    if (multiplyByAspect.Measure == null)
                    {

                    }
                    else if (multiplyByAspect.Measure != null)
                    {
                        string result = EvaluateXPathExpression(this, multiplyByAspect.Measure, variableEntries);

                        if (UnitAspectFormula.Augment)
                        {
                            unit.Add(new XElement(xbrli + "measure", result));
                        }
                        else
                        {
                            unit = new XElement(xbrli + "unit", new XElement(xbrli + "measure", result));
                        }
                    }

                }

                foreach (DivideByAspect divideByAspect in UnitAspectFormula.DivideByAspectFormulas)
                {
                    // Unit division rules

                    XElement measureSAV = null;

                    if (divideByAspect.Source != null)
                    {
                        if (variableEntries.Count > 0)
                        {
                            defaultFact = variableEntries[divideByAspect.Source];
                        }

                        if (defaultFact.UnitRef != null) // item is numeric
                        {
                            measureSAV = defaultFact.UnitRef.UnitElement.Elements().FirstOrDefault(); ;
                        }
                    }                   

                    if (divideByAspect.Measure == null)
                    {
                        if (defaultFact.UnitRef != null)
                        {
                            //unit = new XElement(defaultFact.UnitRef.UnitElement);

                            string result = measureSAV.Value;

                            if (this.UnitAspectFormula.Augment)
                            {
                                if (unit.Elements(xbrli + "measure").Where(e => e.Value == result).Count() > 0)
                                {
                                    unit.Elements(xbrli + "measure").Where(e => e.Value == result).Remove();
                                }
                                else
                                {
                                    unit.Add(new XElement(xbrli + "measure", result));
                                }
                            }
                            else
                            {
                                unit = new XElement(xbrli + "unit", new XElement(xbrli + "measure", result));
                            }
                        }
                    }
                    else
                    {
                        string result = EvaluateXPathExpression(this, divideByAspect.Measure, variableEntries);

                        if (this.UnitAspectFormula.Augment)
                        {
                            if (unit.Elements(xbrli + "measure").Where(e => e.Value == result).Count() > 0)
                            {
                                unit.Elements(xbrli + "measure").Where(e => e.Value == result).Remove();
                            }
                            else
                            {
                                unit.Add(new XElement(xbrli + "measure", result));
                            }
                        }
                        else
                        {
                            unit = new XElement(xbrli + "unit", new XElement(xbrli + "measure", result));
                        }
                    }

                }

                if (defaultFact == null && !UnitAspectFormula.Element.HasElements)
                {
                    log.Error("Formula error: The formula: " + this.Label + " . The  unit rule does not have any child elements and does not have a SAV.");
                    dts.ValidatedFormula = false;
                    return false;
                }

                FormulaProcessor.xbrl.Add(unit);
            }
            else if (UnitAspectFormula == null)
            {
                //if (this.ImplicitFiltering && this.Source != null)

                FormulaProcessor.xbrl.Add(unit);
                  
            }

            return true;
        }


        internal bool BuildOutputFact(FormulaProcessor formulaProcessor, XdmValue xdmValue, ref XElement itemSAV, List<FactItem> cartesianProductItem, Dictionary<string, List<FactItem>> variableItems, Dictionary<string, List<List<FactItem>>> variableSequences, Dictionary<string, FactItem> variableEntries, FactItem defaultFact)
        {
            Concept concept = null;

            // Concept rules

            if (!BuildConceptAspect(ref itemSAV, cartesianProductItem, variableItems, variableEntries, ref concept, defaultFact))
            {
                return false;
            }

            // Value rules

            if (!BuildValueAspect(xdmValue, itemSAV))
            {
                return false;
            }

            // Accuracy rules

            BuildAccuracyAspects(itemSAV, variableEntries, concept);

            return true;
        }


        private bool BuildConceptAspect(ref XElement itemSAV, List<FactItem> cartesianProductItem, Dictionary<string, List<FactItem>> variableItems, Dictionary<string, FactItem> variableEntries, ref Concept concept, FactItem defaultFact)
        {
            if (this.ConceptAspectFormula != null)
            {
                if (this.ConceptAspectFormula.Qname != null)
                {
                    XElement newItem = new XElement(this.ConceptAspectFormula.Qname);
                    itemSAV = newItem;

                    concept = dts.GetConceptByName(ConceptAspectFormula.Qname.LocalName);
                }
                else if (this.ConceptAspectFormula.QnameExpression != null)
                {
                    string result = EvaluateXPathExpression(this, this.ConceptAspectFormula.QnameExpression, variableEntries);

                    string prefix = result.GetPrefix();
                    string localName = result.GetLocalName();

                    XNamespace ns = this.ParentDocument.ThisXDocument.Root.GetNamespaceOfPrefix(prefix);

                    XElement newItem = new XElement(ns + localName);
                    itemSAV = newItem;

                    concept = dts.GetConceptByName(result.GetLocalName());
                }
                else
                {
                    if (ConceptAspectFormula.Source == null)
                    {
                        log.Error("Formula error: The formula: " + this.Label + " .The concept rule does not have a nearest source and does not have a child element. ");
                        dts.ValidatedFormula = false;
                        return false;
                    }

                    if (variableEntries.Count > 0)
                    {
                        itemSAV = new XElement(variableEntries[ConceptAspectFormula.Source].FactElement);
                    }
                    else
                    {
                        XNamespace ns = defaultFact.Namespace;
                        itemSAV = new XElement( ns + defaultFact.Name);
                    }
                }
            }
            else
            {
                string prefix = this.Source.GetPrefix();

                FactVariable factVariable = null;

                if (this.Source != null && !this.Source.GetLocalName().Equals("uncovered"))
                {
                    factVariable = this.GetFactVariable(this.Source, this);
                }
                else
                {
                    for (int i = 0; i < variableItems.Count; i++)
                    {
                        factVariable = this.GetFactVariable(variableItems.ElementAt(i).Key, this);

                        if (!((factVariable.CoveredAspects.Intersect(this.CoveredAspects)).Count() > 0))
                        {
                            break;
                        }
                    }
                }

                //FactItem factVariableItem = this.FilteringFacts(this, factVariable).FirstOrDefault();
                FactItem factVariableItem = cartesianProductItem.ElementAt(0);

                XNamespace ns = null;

                if (prefix == "")
                {
                    ns = this.ParentDocument.ThisXDocument.Root.GetDefaultNamespace();
                }
                else
                {
                    ns = this.ParentDocument.ThisXDocument.Root.GetNamespaceOfPrefix(prefix);
                }

                concept = factVariableItem.Concept;

                itemSAV = new XElement(ns + concept.Name);
            }

            return true;
        }


        private bool BuildValueAspect(XdmValue xdmValue, XElement itemSAV)
        {
            if (xdmValue == null)
            {
                itemSAV.Value = "";
                XNamespace xsi = "http://www.w3.org/2001/XMLSchema-instance";

                if (FormulaProcessor.xbrl.Attribute(XNamespace.Xmlns + "xsi") == null)
                {
                    FormulaProcessor.xbrl.Add(new XAttribute(XNamespace.Xmlns + "xsi", xsi));
                }

                itemSAV.Add(new XAttribute(xsi + "nil", true));

                RemoveAttribute(itemSAV, "precision");
                RemoveAttribute(itemSAV, "decimals");
                //formulaProcessor.ItemsFormula.Add(itemSAV);
                return true;
            }
            else if (xdmValue.Count == 1)
            {
                IEnumerator e = xdmValue.GetEnumerator();
                bool exists = e.MoveNext();

                if (exists)
                {
                    XdmItem node = (XdmItem)e.Current;

                    if (node.IsAtomic())
                    {
                        itemSAV.Value = node.ToString();
                    }
                    else
                    {
                        string value = (node as XdmNode).StringValue;

                        if (value != string.Empty)
                        {
                            itemSAV.Value = value;
                        }
                        else
                        {
                            XNamespace xsi = "http://www.w3.org/2001/XMLSchema-instance";

                            if (FormulaProcessor.xbrl.Attribute(XNamespace.Xmlns + "xsi") == null)
                            {
                                FormulaProcessor.xbrl.Add(new XAttribute(XNamespace.Xmlns + "xsi", xsi));
                            }

                            itemSAV.Add(new XAttribute(xsi + "nil", true));
                        }
                    }
                }
            }
            else
            {
                log.Error("Formula error: The formula: " + this.Label + " .The value expression evaluates to produce a sequence that contains more than one item");
                dts.ValidatedFormula = false;
                return false;
            }

            return true;
        }


        private void BuildAccuracyAspects(XElement itemSAV, Dictionary<string, FactItem> variableEntries, Concept concept)
        {
            if (this.DecimalAspect != null)
            {
                this.Decimals = EvaluateXPathExpression(this, this.DecimalAspect, variableEntries);

                XAttribute decimalsAttr = new XAttribute("decimals", this.Decimals);
                itemSAV.Add(decimalsAttr);
            }
            else if (this.PrecisionAspect != null)
            {
                this.Precision = EvaluateXPathExpression(this, this.PrecisionAspect, variableEntries);

                XAttribute precisionAttr = new XAttribute("precision", this.Precision);
                itemSAV.Add(precisionAttr);
            }
            else if (concept.SchemaElement.ElementSchemaType.IsNumeric() &&
                this.DecimalAspect == null && this.PrecisionAspect == null)
            {
                this.Precision = "0";
                XAttribute precisionAttr = new XAttribute("precision", this.Precision);
                itemSAV.Add(precisionAttr);
            }
        }


        internal string EvaluateXPathExpression(VariableSet variableSet, string expression, Dictionary<string, FactItem> variableEntries)
        {
            RegisterExtensionFunctions();

            XPathSelector selector = compiler.Compile(expression).Load();

            BindVariableValue(variableEntries, this.parameterDictionary, selector, variableAsSequenceEntries);

            SetContextItem(processor, selector, dts.Instance.ThisXDocument.Root.GetXmlNode());

            XdmValue xdmValue = selector.Evaluate();

            if (xdmValue.Count > 1)
            {
                log.Error("Formula error: The formula: " + this.Label + " . The result of evaluating the XPath expression in a typed dimension rule is not a sequence containing a single element node.");
                dts.ValidatedFormula = false;
            }

            string result = selector.EvaluateSingle().ToString();

            return result;
        }


        private string EvaluateXPathExpression(VariableSet variableSet, string expression, FactItem item)
        {
            RegisterExtensionFunctions();
            
            //DeclareVariables(variableSet, compiler);

            if (dts.Instance != null)
            {
                compiler.BaseUri = GetPath(dts.Instance.ThisXDocument.BaseUri);
            }

            XPathSelector selector = compiler.Compile(expression).Load();

            XNamespace ns = item.Namespace;

            foreach (VariableSet variable in variableSet.VariableSetArcs)
            {
                XmlNode xmlNode = item.FactElement.GetXmlNode();
                XdmNode node = processor.NewDocumentBuilder().Build(xmlNode);
                IEnumerator ee = node.EnumerateAxis(XdmAxis.Child);

                XdmNode n = (XdmNode)ee.Current;

                string prefix = variable.Name.GetPrefix();

                if (prefix.Length > 0)
                {
                    selector.SetVariable(new QName(namespaces[prefix], variable.Name), n);
                }
                else
                {
                    selector.SetVariable(new QName(variable.Name), n);
                }
            }

            SetContextItem(processor, selector, dts.Instance.ThisXDocument.Root.GetXmlNode());

            string result = selector.EvaluateSingle().ToString();
            //XdmValue result2 = selector.Evaluate();

            return result;
        }


        private static void RegisterExtensionFunctions()
        {
            processor.RegisterExtensionFunction(new PrecisionXfi());

            // ===================================== Contexts and units ==================================================
            processor.RegisterExtensionFunction(new ContextXfi());
            processor.RegisterExtensionFunction(new UnitXfi());
            processor.RegisterExtensionFunction(new UnitNumeratorXfi());
            processor.RegisterExtensionFunction(new UnitDenominatorXfi());
            processor.RegisterExtensionFunction(new MeasureNameXfi());
            processor.RegisterExtensionFunction(new PeriodXfi());
            processor.RegisterExtensionFunction(new ContextPeriodXfi());
            processor.RegisterExtensionFunction(new IsStartEndPeriodXfi());
            processor.RegisterExtensionFunction(new IsForeverPeriodXfi());
            processor.RegisterExtensionFunction(new IsDurationPeriodXfi());
            processor.RegisterExtensionFunction(new IsInstantPeriodXfi());
            processor.RegisterExtensionFunction(new PeriodStartXfi());
            processor.RegisterExtensionFunction(new PeriodEndXfi());
            processor.RegisterExtensionFunction(new PeriodInstantXfi());
            processor.RegisterExtensionFunction(new EntityXfi());
            processor.RegisterExtensionFunction(new ContextEntityXfi());
            processor.RegisterExtensionFunction(new IdentifierXfi());
            processor.RegisterExtensionFunction(new ContextIdentifierXfi());
            processor.RegisterExtensionFunction(new EntityIdentifierXfi());
            processor.RegisterExtensionFunction(new IdentifierValueXfi());
            processor.RegisterExtensionFunction(new IdentifierSchemeXfi());
            processor.RegisterExtensionFunction(new SegmentXfi());
            processor.RegisterExtensionFunction(new EntitySegmentXfi());
            processor.RegisterExtensionFunction(new ContextSegmentXfi());
            processor.RegisterExtensionFunction(new ScenarioXfi());
            processor.RegisterExtensionFunction(new ContextScenarioXfi());

            // =============================== Facts and tuples ===========================================
            processor.RegisterExtensionFunction(new ItemsInInstanceXfi());
            processor.RegisterExtensionFunction(new TuplesInInstanceXfi());
            processor.RegisterExtensionFunction(new ItemsInTupleXfi());
            processor.RegisterExtensionFunction(new TuplesInTupleXfi());

            // ======================================== Predicates ==================================================
            processor.RegisterExtensionFunction(new IdenticalNodesXfi());
            processor.RegisterExtensionFunction(new SEqualXfi());
            processor.RegisterExtensionFunction(new UEqualXfi());

            // ======================================== Other Functions ==================================================
            processor.RegisterExtensionFunction(new FactExplicitDimensionValueXfi());
            processor.RegisterExtensionFunction(new FactDimensionSequal2Xfi());
        }


        private static void RemoveAttribute(XElement containerElement, string attr)
        {
            XAttribute attrExistent = containerElement.Attribute(attr);

            if (attrExistent != null)
            {
                attrExistent.Remove();
            }
        }


        private static void RemoveElement(XElement containerElement, XName element)
        {
            XElement elementExistent = containerElement.Element(element);

            if (elementExistent != null)
            {
                elementExistent.RemoveWithNextWhitespace();
            }
        }


        private List<FactItem> GetItemsBySource(string source, Dictionary<Tuple<string, int?, bool>, List<FactItem>> factsFiltering)
        {
            List<FactItem> items = null;
            foreach (Tuple<string, int?, bool> tuple in factsFiltering.Keys)
            {
                if (tuple.Item1.Equals(source))
                {
                    items = factsFiltering[tuple];
                }
            }

            return items;
        }


        //private static string EvaluateXPathExpression(string expression)
        //{
        //    // Create a Processor instance.
        //    Processor processor = new Processor();

        //    // Create the XPath expression.
        //    XPathCompiler compiler = processor.NewXPathCompiler();

        //    XPathSelector selector = compiler.Compile(expression).Load();

        //    string result = selector.EvaluateSingle().ToString();

        //    return result;
        //}       


        private string GetPrefix(string qname)
        {
            string[] values = qname.Split(':');

            if (values.Count() > 1)
            {
                return values[0];
            }
            else
            {
                return "";
            }
        }

        private string GetLocalName(string qname)
        {
            string[] values = qname.Split(':');

            if (values.Count() > 1)
            {
                return values[1];
            }
            else
            {
                return values[0];
            }
        }


        private string GetPath(string uriBase)
        {
            string path = uriBase;
            int LastPathSeparator = uriBase.LastIndexOf('/');

            if (LastPathSeparator == -1)
            {
                LastPathSeparator = uriBase.LastIndexOf('\\');
                if (LastPathSeparator != -1)
                {
                    path = uriBase.Substring(0, LastPathSeparator + 1);
                }
            }

            path = uriBase.Substring(0, LastPathSeparator + 1);
            return path;
        }

        internal List<ConsecutiveRelationshipsSet> GetConsecutiveRelationshipsSets(Concept primaryItem, string DRSRole)
        {
            List<ConsecutiveRelationshipsSet> temp = new List<ConsecutiveRelationshipsSet>();

            foreach (DimensionalRelationshipSet drs in dts.DtsDimension.DimensionalRelationshipSets)
            {
                foreach (ConsecutiveRelationshipsSet crs in drs.ConsecutiveRelationshipsSets)
                {
                    if (primaryItem.Id.Equals(crs.HasHypercube.PrimaryItem.Id) && drs.DRSRole.Equals(DRSRole))
                    {
                        temp.Add(crs);
                    }
                }
            }

            return temp;
        }


        internal List<ConsecutiveRelationshipsSet> GetCRSInherited(Concept primaryItem, string DRSRole)
        {
            List<ConsecutiveRelationshipsSet> crsTemp = new List<ConsecutiveRelationshipsSet>();

            foreach (Inheritance inheritance in dts.DtsDimension.Inheritances)
            {
                foreach (Concept conceptInherited in inheritance.PrimaryItemsInherited)
                {
                    if (conceptInherited.Id.Equals(primaryItem.Id) && inheritance.SourceRole.Equals(DRSRole))
                    {
                        List<ConsecutiveRelationshipsSet> crss = GetConsecutiveRelationshipsSets(inheritance.PrimaryItem, DRSRole);

                        if (crss.Count > 0)
                        {
                            crsTemp.AddRange(crss);
                        }
                    }
                }
            }

            return crsTemp;
        }
    }


    
}
